IPC trick(Signal & Socket)

자세한 code는 SYSTEM PROGRAMMING_IPC 참고
IPC 기법이지만, 이외에도 많이 사용되는  기술(시그널, 소켓)
시그널(Signal)
유닉스에서 30년 이상 사용된 전통적인 기법
커널 또는 프로세스에서 다른 프로세스에 어떤 이벤트가 발생되었는지 알려주는 기법
프로세스 관련 코드에 관련 시그널 핸드러를 등록해서, 해당 시그널 처리 실행
    1. 시그널 무시
    2. 시그널 블록(블록을 푸는 순간, 프로세스에 해당 시그널 전달)
    3. 등록된 시그널 핸들러로 특정 동작 수행
    4. 등록된 시그널 핸들러가 없다면, 커널에서 기본 동작 수행

시그널은 인터럽트의 일종이다. 시그널을 인터럽트라고 부르기도 함
생각)
인터럽트가 시그널보다 더 큰 개념이라고 생각할 수 있다.
인터럽트가 발생하게 되면 해당 CPU에서 실행중이던, 프로세스를 일단 중지해야 하기 때문에 프로세스를 중지 시키는데 시그널이 사용되며,
해당 작업을 끝내고 다시 프로세스를 실행 시키는데 시그널이 사용된다.—>커널은 따로 시그널을 사용하지 않는다. 시그널은 사용자가
프로세스를 제어하기 위해 운영체제에서 제공하는 시스템콜로 보임

시그널 또한 커널 영역에서 수행되는 명령어기 때문에 시그널은 시스템 콜이라고 볼 수 있다.
주요 시그널: 기본동작
SIGKILL: 프로세스를 죽여라(슈퍼관리자가 사용하는 시그널로, 프로세스는 어떤 겨우든 죽도록 되어 있다.)
SIGALARM: 알람을 발생한다.
SIGSTP: 프로세스를 멈춰라(ctrl+Z)
SIGCONT: 멈춰진프로세스를 실행해라
SIGINT: 프로세스에 인터럽트를 보내서 프로세스를 죽여라(ctrl+C)
SIGSEGV: 프로세스가 다른 메모리영역을 침범했다.
SIGUSR1, SIGUSR2: 기본동작X - 프로그램 프로세스에서 특별한 동작을 하도록 지정 가능

시그널 종류: kill-l
(mac OS)

HUP INT QUIT ILL TRAP ABRT EMT FPE KILL BUS SEGV SYS PIPE ALRM TERM URG STOP TSTP CONT CHLD TTIN TTOU IO XCPU XFSZ VTALRM PROF WINCH INFO USR1 USR2


kill -9 1001        // 1001 프로세스를 SIGKILL(SIGKILL은 9번)


(LINUX)
시그널 핸들러 등록 및 핸들러 구현
static void signal_handler(int signo){
printf("Catch SIGINT!\n");
exit(EXIT_SUCCESS);
}
int main(void){
if(signal(SIGINT, signal_handler)==SIG_ERR){ // SIGINT
printf("Can't catch SIGINT!\n");
exit(EXIT_FAILURE);
}
for(;;)
pause();
return 0;
}
시그널 핸들러 무시
int main(void){
if(signal(SIGINT, SIG_IGN)==SIG_ERR){
printf("Can't catch SIGINT!\n");
exit(EXIT_FAILURE);
}
for(;;)
pause();
return 0;
}
PCB에 대항 프로세스가 블록 또는 처리해야 하는 시그널 관련 정보를 관리하는 공간이 있다.
pending: 처리를 대기 중인 시그널
sigpending
blocked: block된 시그널
sig: 각각의 sig에 대한 동작(handler or 기본 동작 정의)
참고)
  • 기본적으로 시그널 핸들러는 다음과 같은 4가지 동작 중 하나를 수행
    • terminate 프로세스 종료
    • ignore 시그널 무시
    • terminate and core dump 프로세스가 종료되고 그 당시의 레지스터와 메모리를 덤프
    • stop or continue 프로세스 중단 또는 계속 실행
  • 프로세스의 실행을 중단시키는 SIGSTOP 시그널과 프로세스를 종료시키는 SIGKILL 시그널은 무시할 수 없음
  • 그 외에 모든 시그널은 무시할 수 있음
  • 시그널이 여러 개 전달될 때, 임의의 순서로 전달되며, 핸들러로 처리할 때도 임의의 순서로 진행됨
  • 프로세스는 시스템 콜을 이용해서 시그널을 전달하거나 핸들링하는 등 시그널 관련 작업을 수행할 수 있음


  • 커널은 프로세스의 task_struct 구조체에 저장된 정보를 사용해서 시그널 기능을 처리
    • 시그널의 개수는 프로세서의 워드(word) 크기에 제한되는데, 32비트 프로세서는 32개, 64비트 프로세서는 64개
    • 현재 처리 대기중인 시그널은 sig 항목에 비트를 1로 설정 (정수 자료형은 2^32 또는 2^64 비트, 프로세서마다 다름)
    • 블럭된 시그널은 signal_blocked 항목에 비트를 1로 설정
      • SIGSTOP과 SIGKILL을 제외한 모든 시그널은 블럭킹 가능
      • 블럭된 시그널이 발생하면, 블럭을 해제할 때까지 대기 상태
      • 블럭이 해제되면 핸들러가 호출됨
      • 핸들러가 종료되면, 커널은 blocked 항목을 원래 값으로 되돌려 놓기 위해 정리용 루틴을 호출하는데, 여기서 시그널을 받은 프로세스의 콜 스택(call stack)에 저장된 원래 blocked 값을 꺼내서 복구
      • 여러 개의 시그널 핸들러가 호출되는 경우, 이 루틴들은 콜 스택에 쌓여서 마지막에 정리용 루틴이 호출되도록 함
    • 시그널 핸들러는 sigaction 구조체에서 참조하며, 각 시그널의 핸들러는 signal_struct 의 action 이라는 sigaction 구조체 배열에 시그널 번호를 인덱스로 저장되어 있음
      • sigaction 구조체에서 sigflags 를 이용해 해당 시그널을 무시할 지 또는 커널이 시그널을 대신 처리할 지를 결정
      • 커널에 위임한다는 것은 기본 동작을 의미 (위의 4가지 동작 중 하나)
      • 시그널 핸들러는 시스템 콜로 변경할 수 있으며, 이 시스템 콜은 해당 시그널의 sigaction과 blocked 변수를 수정
  • 시그널 관련 시스템 콜
    • kill() 은 다른 프로세스로 시그널을 전달(프로세스 종료 아님)하며, signal() 은 해당 프로세스의 시그널 핸들러를 변경
커널모드에서 사용자모드로 전환되기 직전 해당 프로세스의 PCB의 signal을 확인해서 처리할 커널 함수를 처리후 사용자모드로 전환
기본 동작이 아니라 특정 동작을 정의해 놓았다면(특정 동작은 사용자 모드에 정의되어 있음), 사용자 모드로 전환됨과 동시에 실행

커널에서 사용자모드 전환시 시그널 처리
https://movefast.tistory.com/344
소켓(Socket)
네트워크 통신을 위한 기술(네트워크 시스템을 사용하기 위한 시스템 콜)
기본적으로는 클라이언트와 서버등 두개의 다른 컴퓨터간의 네트워크 기반 통신을 위한 기술이다.

소켓을 하나의 컴퓨터 안에서, 두 개의 프로세스간에 통신 기법으로 사용 가능하다.
운영체제(OS) 부트캠프: 프로세스간 커뮤니케이션 (IPC) - 잔재미코딩
Linux
#incldue <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#incldue <stdlib.h>
#define MAXLINE 1024
int main(int argc, char **argv){
strcut sockaddr_in serveraddr;
int server_sockfd;
int client_len;
char buf[MAXLINE];
if((server_sockfd=socket(AF_INET, SOCK_STREAM, 0))==-1){
perror("error: ");
return 1;
}
serveraddr.sin_family=AF_INET;
serveraddr.sin_addr.s_addr=inet_addr("127.0.0.1");
serveraddr.sin_port=htons(4000);
client_len=sizeof(serveraddr);
if(connect(server_sockfd, (struct sockaddr*)&serveraddr, client_len)==-1){
perror("connect error: ");
return 1;
}
memset(buf, 0x00, MAXLINE);
read(0, buf, MAXLINE);
if(write(server_sockfd, buf, MAXLINE)<=0){
perror("write error:");
return 1;
}
memset(buf, 0x00, MAXLINE);
if(read(server_sockfd, buf, MAXLINE)<=0){
perror("read error: ");
return 1;
}
close(server_sockfd);
printf("server: %s\n", buf);
return 0;
}